<?php

namespace App\Console\Commands;

use App\Models\Period;
use App\Models\Staff;
use App\Models\StaffDormAdjust;
use App\Models\StaffDormAdjustSniffer;
use App\Models\StaffDormHistory;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Arr;

class CalcStaffDorms extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'calc:dorms {period_id} {staff_id?}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $period_id = $this->argument('period_id');
        $period = Period::find($period_id);

        if($period && $period->status!=2){
            $end = $period->end_date;
            $staffId=$this->argument('staff_id');

            //对staffDormAdjust_count <> handled_adjust的才执行下面的方法，否则就插入一条本期的一个全宿的记录。
            if($staffId)
            {
                //只计算一个人
                $this->info('build one');
                $this->build_one($staffId,$end);
            }
            else{
                $staffs_not_adjust = Staff::query()->whereNotNull('latest_in_date')->where('latest_in_date','<',$period->start_date)->get();
                //入住时间小于或等于期初时间的，说明没有做过调整，那么就创建一条完整的期间住宿记录
                foreach ($staffs_not_adjust as $staff){
                    $this->build_fully_hist($staff,$period);
                }
                //看触发器记录表中是否有未处理的变更，有则只对这些员工进行住宿记录的重建(暂时弃用)
                /*
                $adjust_sniffers = StaffDormAdjustSniffer::query()->unhandled()->orderBy('created_at','desc')->get();
                $adjust_staff_ids = $adjust_sniffers->pluck('staff_id')->unique();
                if(count($adjust_staff_ids)){
                    //有未处理的触发器记录，则走新的计算模型
                    foreach ($adjust_staff_ids as $adjust_staff_id) {
                        //触发器模式下会将重建的范围拉大到最后一个周期的截止日期。
                        $maxPeriod = Period::query()->orderBy('end_date','desc')->first();
                        $this->build_one($adjust_staff_id,$maxPeriod->end_date);
                        StaffDormAdjustSniffer::query()->where('staff_id',$adjust_staff_id)->where('handled_status',0)->update(['handled_status'=>1]);
                    }
                }else{
                */
                    $adjust_staff_ids = StaffDormAdjust::where('adjust_date', '>=', $period->start_date)->pluck('staff_id');

                    //本期之后有调整的，重建住宿历史
                    foreach ($adjust_staff_ids as $staff_id){
                        $this->warn("going to build dorm history of $staff_id");
                        $this->build_one($staff_id,$period->end_date);
                        /*
                    }
                    */
                }
            }
            $this->info('calc finished');
        }

    }

    /**
     * @param $staffId
     * @param $period_end
     * 重建周期末以前的入住记录
     */
    public function build_one($staffId, $period_end){
        $staff = Staff::with('staffDormAdjusts')->find($staffId);
        if($staff){
            StaffDormHistory::query()->where('staff_id',$staffId)->where('to_date','<=',$period_end)->delete();
            $adjusts = $staff->staffDormAdjusts;
            $first_adjust = $adjusts->pop();
            $prev_adjust = $first_adjust;
            //第一个宿管记录必须为入住，否则不予处理
            if($first_adjust && $first_adjust->adjust_type=='in'){
                $still_in = true;
                $current_dorm = $prev_adjust->dormTo;
                while ($next_adjust = $adjusts->pop()){
                    //调整时间大于当前计算周期，则不予计算
                    if($next_adjust->adjust_date > $period_end){
                        $this->warn("adjust date $next_adjust->adjust_date bigger than period_end $period_end build break");
                        break;
                    }
                    //已经归档（period_id不为空）的宿管（调整）记录不需要再次创建
                    if($prev_adjust->period_id==null || $next_adjust->period_id==null){
                        //两次调整之间的住宿历史的创建
                        $this->build_hist($staffId, optional($current_dorm)->id, Carbon::parse($prev_adjust->adjust_date), Carbon::parse($next_adjust->adjust_date));
                        $this->info("staff_id=$staffId build dorm history from $prev_adjust->adjust_date to $next_adjust->adjust_date success");
                    }else{
                        $this->warn("adjust between period of $prev_adjust->period_id and $next_adjust->period_id  has archive, build hist cancel");
                    }

                    $type=$next_adjust->adjust_type;
                    if($type=='out'){
                        $current_dorm = null;
                        $still_in =false;
                    }else{
                        $current_dorm = $next_adjust->dormTo;
                        $still_in = true;
                    }
                    $prev_adjust = $next_adjust;
                }
                if($still_in) {
                    $this->build_hist($staffId,optional($current_dorm)->id,Carbon::parse($prev_adjust->adjust_date),Carbon::parse($period_end));
                    $this->info("staff_id=$staffId build dorm history from $prev_adjust->adjust_date to $period_end success");
                }

            }else{
                $this->warn("staff_id=$staffId first adjust is not in, ignore build one");
            }
        }
    }

    public function build_hist($staff_id, $dorm_id, Carbon $from_date, Carbon $to_date){
        if($dorm_id && $to_date>=$from_date){
            $months =($to_date->year-$from_date->year)*12+($to_date->month-$from_date->month);
            if($months==0){
                //不跨月
                $span = $to_date->diffInDays($from_date)+1;
                if($span>1){
                    StaffDormHistory::query()->firstOrCreate(['staff_id'=>$staff_id,'dorm_id'=>$dorm_id,'year'=>$from_date->year,'month'=>$from_date->month,'days'=>$span,'from_date'=>$from_date,'to_date'=>$to_date]);
                }
            }else{
                //跨月
                $from = $from_date;
                for($i=0;$i<=$months;$i++){
                    $to = Carbon::create($from->year,$from->month,1)->lastOfMonth();
                    $to = $to_date<$to?$to_date:$to;
//                    $this->info("$from --> $to");
                    $span = $to->diffInDays($from)+1;
                    if($span>1)
                        StaffDormHistory::query()->firstOrCreate(['staff_id'=>$staff_id,'dorm_id'=>$dorm_id,'year'=>$from->year,'month'=>$from->month,'days'=>$span,'from_date'=>$from,'to_date'=>$to]);
                    $from = Carbon::create($from->year,$from->month+1,1)->startOfDay();

                }
            }

//            $this->info("dorm_id=$dorm_id $from_date --> $to_date months=$months built");
        }else{
            $this->warn("dorm_id=$dorm_id from $from_date -->  to $to_date ignore");
        }

    }

    public function build_fully_hist(Staff $staff,Period $period){
        if($staff->dorm_id){
            //满宿的情况下，同一个月不可能出现多条住宿记录的，有的话肯定是垃圾数据，需要先删除。
            StaffDormHistory::query()->where([['staff_id',$staff->id],['year',$period->year],['month',$period->month]])->delete();
            StaffDormHistory::query()->create(['staff_id'=>$staff->id,'dorm_id'=>$staff->dorm_id,'year'=>$period->year,'month'=>$period->month,'days'=>$period->period_day_span,'from_date'=>$period->start_date,'to_date'=>$period->end_date]);
        }else{
            $this->warn('staff has move out');
        }

    }
}
